﻿using System.Collections.Concurrent;
using System.Net;
using System.Net.Sockets;
using System.Runtime.CompilerServices;
using UtilZ.Dotnet.Ex.Base;
using UtilZ.Dotnet.Ex.Log;
using UtilZ.Dotnet.Ex.Model;

namespace WinFormApp
{
    public partial class UCUdpRevControl : UserControl
    {
        public UCUdpRevControl()
        {
            InitializeComponent();
        }

        private System.Windows.Forms.Timer _showRevDataLengthInfoTimer;
        private void UCUdpRevControl_Load(object sender, EventArgs e)
        {
            this._showRevDataLengthInfoTimer = new System.Windows.Forms.Timer();
            this._showRevDataLengthInfoTimer.Interval = 1000;
            this._showRevDataLengthInfoTimer.Tick += _showRevDataLengthInfoTimer_Tick;

            IPAddress[] ipaddrArr = NetHelper.GetAllIPV4();
            foreach (var ipaddr in ipaddrArr)
            {
                comboBoxRcvDataIP.Items.Add(ipaddr);
            }
            if (comboBoxRcvDataIP.Items.Count > 0)
            {
                comboBoxRcvDataIP.SelectedIndex = 0;
            }

            //txtRcvDataIP.Text = "192.168.1.5";
            numRcvDataPort.Value = 8064;
        }



        private UdpReceiveDataBLLNetUdp _receiveDataBLL = null;


        private void ReciveCompleted()
        {
            try
            {
                if (this.InvokeRequired)
                {
                    this.Invoke(this.ReciveCompleted);
                }
                else
                {
                    Loger.Info("已停止接收数据");
                    this.SetControlEnable(true);
                    this._showRevDataLengthInfoTimer.Stop();
                    this._showRevDataLengthInfoTimer_Tick(this, null);
                    this._receiveDataBLL.Dispose();
                    this._receiveDataBLL = null;
                    this._flag = false;
                }
            }
            catch (Exception ex)
            {
                Loger.Error(ex);
            }
        }

        private void SetControlEnable(bool enable)
        {
            comboBoxRcvDataIP.Enabled = enable;
            numRcvDataPort.Enabled = enable;

            txtFileDataStorePath.Enabled = enable;
            btnChoiceOCServiceDataStorePath.Enabled = enable;
            numRcvDuration.Enabled = enable;
            numRcvFileSize.Enabled = enable;
            checkBoxWriteFile.Enabled = enable;


            if (enable)
            {
                btnStartReceive.Text = "开始";
            }
            else
            {
                btnStartReceive.Text = "停止";
            }
        }



        private string _startTime = string.Empty;
        private long _startRcvTimestamp = 0L;
        private const string _RCV_TIME_FORMAT = "HH:mm:ss.fff";



        private bool _flag = false;
        private void btnStartReceive_Click(object sender, EventArgs e)
        {
            try
            {
                if (this._flag)
                {
                    if (this._receiveDataBLL != null)
                    {
                        this._receiveDataBLL.Stop();
                    }
                    //this._flag = false;
                }
                else
                {

                    if (this._receiveDataBLL != null)
                    {
                        this._receiveDataBLL.Dispose();
                    }

                    IPEndPoint localEndPoint = new IPEndPoint(IPAddress.Parse(comboBoxRcvDataIP.Text.Trim()), (int)numRcvDataPort.Value);
                    int packageHeadSize = (int)numPackageHeadSize.Value;
                    int udpPackageMaxLength = (int)numUdpPackageMaxLength.Value;
                    this._receiveDataBLL = new UdpReceiveDataBLLNetUdp(localEndPoint, packageHeadSize, udpPackageMaxLength, checkBoxDataFramMode.Checked, this.ReciveCompleted);

                    long singleFileByteLimit = UnitConverter.MByteToByte((double)numRcvFileSize.Value);
                    int durationMilliseconds = (int)(numRcvDuration.Value * 1000);


                    this._lastRcvLength = 0L;
                    this._lastWriteDataLength = 0L;
                    this._lastRcvTimestamp = TimeEx.GetTimestamp();

                    this._receiveDataBLL.Start(checkBoxWriteFile.Checked, txtFileDataStorePath.Text.Trim(), singleFileByteLimit, durationMilliseconds);
                    this._showRevDataLengthInfoTimer.Start();

                    var time = DateTime.Now;
                    this._startRcvTimestamp = TimeEx.GetTimestamp(time);
                    this._startTime = time.ToString(_RCV_TIME_FORMAT);
                    Loger.Info("已开始接收数据");

                    this.SetControlEnable(false);
                    this._flag = true;
                }
            }
            catch (Exception ex)
            {
                Loger.Error(ex);
            }
        }



        private long _lastRcvLength = 0L;
        private long _lastWriteDataLength = 0L;
        private long _lastRcvTimestamp = 0L;

        private void _showRevDataLengthInfoTimer_Tick(object sender, EventArgs e)
        {
            //const int K = 1024;
            //const int K = 1000;
            const int M = 1000000;

            var time = DateTime.Now;
            var currenttimestamp = TimeEx.GetTimestamp(time);
            var durationMs = currenttimestamp - this._startRcvTimestamp;
            double seonds = (double)durationMs / 1000;

            labelRcvTimeInfo.Text = $"接收时间：{this._startTime}    -   {time.ToString(_RCV_TIME_FORMAT)}    接收时长：{durationMs}ms，{seonds}s";

            var receiveCount = this._receiveDataBLL.ReceiveCount;
            labelReceiveCount.Text = $"{durationMs}ms内接收数据次数：{receiveCount}";

            var rcvDataLength = this._receiveDataBLL.ReceiveDataLength;
            labelReceiveLength.Text = $"{durationMs}ms内接收到数据长度：{Math.Round((double)rcvDataLength / M, 3)}MB，{rcvDataLength}Byte";


            var writeCount = this._receiveDataBLL.WriteCount;
            labelWriteLength.Text = $"{durationMs}ms内写入文件数据：{writeCount}次";


            var writeDataLength = this._receiveDataBLL.WriteDataLength;
            labelWriteLength.Text = $"{durationMs}ms内写入文件数据长度：{Math.Round((double)writeDataLength / M, 3)}MB，{writeDataLength}Byte";


            seonds = (double)(currenttimestamp - this._lastRcvTimestamp) / 1000;
            if (seonds < 0.001)
            {
                labelRcvSpeed.Text = "平均每秒接收数据大小：0MB，0Byte";
                labelWriteSpeed.Text = "平均每秒写入磁盘数据大小：0MB，0Byte";
            }
            else
            {
                long newLength = rcvDataLength - this._lastRcvLength;
                labelRcvSpeed.Text = $"平均每秒接收数据大小：{Math.Round(newLength / M / seonds, 3)}MB，{Math.Ceiling(newLength / seonds)}Byte";

                newLength = writeDataLength - this._lastWriteDataLength;
                labelWriteSpeed.Text = $"平均每秒写入磁盘数据大小：{Math.Round(newLength / M / seonds, 3)}MB，{Math.Ceiling(newLength / seonds)}Byte";
            }

            this._lastRcvLength = rcvDataLength;
            this._lastWriteDataLength = writeDataLength;
            this._lastRcvTimestamp = currenttimestamp;
        }



        private void btnChoiceOCServiceDataStorePath_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog();
            if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
            {
                txtFileDataStorePath.Text = folderBrowserDialog.SelectedPath;
            }
        }








        unsafe internal class UdpReceiveDataBLLNetUdp : IDisposable
        {
            private bool _isStoreData = false;
            private string _storeDir = null;
            private long _singleFileByteLimit = 0L;


            private long _receiveCount = 0L;
            public long ReceiveCount
            {
                get { return Interlocked.Read(ref _receiveCount); }
            }

            private long _receiveDataLength = 0L;
            public long ReceiveDataLength
            {
                get { return Interlocked.Read(ref _receiveDataLength); }
            }

            private long _writeDataLength = 0L;
            public long WriteDataLength
            {
                get { return Interlocked.Read(ref _writeDataLength); }
            }
            private long _writeCount = 0L;
            public long WriteCount
            {
                get { return Interlocked.Read(ref _writeCount); }
            }


            private readonly int _packageHeadSize;
            private readonly int _udpPackageMaxLength;
            private readonly Action _reciveCompleted;
            private readonly bool _dataFramMode;


            private readonly int _asyncWriteDataPointerLength;
            private readonly List<UnmanagedMemoryPointer<byte>> _asyncWriteDataPointerList = new List<UnmanagedMemoryPointer<byte>>();
            private readonly ConcurrentQueue<PtrSpan<byte>> _asyncWriteDataSegPool = new ConcurrentQueue<PtrSpan<byte>>();
            private readonly ConcurrentQueue<PtrSpan<byte>> _asyncWaitWriteDataSegPool = new ConcurrentQueue<PtrSpan<byte>>();
            private readonly AutoResetEvent _asyncWaitWriteDataEventHandler = new AutoResetEvent(false);

            private readonly IPEndPoint _localEndPoint;
            private readonly ThreadEx _receiveDataThread;
            private readonly ThreadEx _writeDataThread;
            private readonly ThreadEx _waitRcvEndThread;

            public UdpReceiveDataBLLNetUdp(IPEndPoint localEndPoint, int packageHeadSize, int udpPackageMaxLength, bool dataFramMode, Action reciveCompleted)
            {
                this._packageHeadSize = packageHeadSize;
                this._udpPackageMaxLength = udpPackageMaxLength;
                this._reciveCompleted = reciveCompleted;
                this._dataFramMode = dataFramMode;

                this._asyncWriteDataPointerLength = udpPackageMaxLength * 1000;
                var pointer = new UnmanagedMemoryPointer<byte>(this._asyncWriteDataPointerLength);
                int length = pointer.Length;
                byte* ptr = pointer.DataPtr;
                while (length > 0)
                {
                    this._asyncWriteDataSegPool.Enqueue(new PtrSpan<byte>(ptr, udpPackageMaxLength, udpPackageMaxLength));
                    ptr += udpPackageMaxLength;
                    length -= udpPackageMaxLength;
                }
                this._asyncWriteDataPointerList.Add(pointer);


                this._localEndPoint = localEndPoint;
                this._receiveDataThread = new ThreadEx(this.ReceiveDataThreadMethod, "ReceiveDataThreadMethod");
                this._writeDataThread = new ThreadEx(this.WriteDataThreadMethod, "WriteDataThreadMethod");
                this._waitRcvEndThread = new ThreadEx(this.WaitRcvEndThreadMethod, "等等接收数据结束线程.");
            }



            private void WaitRcvEndThreadMethod(ThreadPara threadPara)
            {
                try
                {
                    int durationMilliseconds = (int)threadPara.Para;
                    threadPara.WaitOne(durationMilliseconds);
                    this._reciveCompleted();
                }
                catch (Exception ex)
                {
                    Loger.Error(ex);
                }
            }




            private UnmanagedMemoryPointer<byte> _pointer = null;
            private int _pointerOffset = 0;
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            private PtrSpan<byte> GetDataSeg()
            {
                if (this._pointer == null)
                {
                    this._pointer = new UnmanagedMemoryPointer<byte>(this._asyncWriteDataPointerLength);
                    this._asyncWriteDataPointerList.Add(this._pointer);
                    this._pointerOffset = 0;
                }

                var dataSeg = new PtrSpan<byte>(this._pointer.DataPtr + this._pointerOffset, this._udpPackageMaxLength, this._udpPackageMaxLength);
                this._pointerOffset += this._udpPackageMaxLength;

                if (this._pointerOffset + this._udpPackageMaxLength >= this._pointer.Length)
                {
                    this._pointer = null;
                    this._pointerOffset = 0;
                }

                return dataSeg;
            }


            private void ReceiveDataThreadMethod(ThreadPara threadPara)
            {
                try
                {
                    using var client = NetHelper.CreateUdpClient(this._localEndPoint, 1000000000, 100);
                    client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
                    EndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);
                    PtrSpan<byte> dataSeg = null;
                    Span<byte> buffer;
                    int revLen;

                    while (!threadPara.Token.IsCancellationRequested)
                    {
                        try
                        {
                            if (dataSeg == null)
                            {
                                if (!this._asyncWriteDataSegPool.TryDequeue(out dataSeg))
                                {
                                    dataSeg = this.GetDataSeg();
                                }
                            }

                            buffer = new Span<byte>(dataSeg.DataPtr, dataSeg.Capcity);
                            revLen = client.ReceiveFrom(buffer, ref remoteEP);
                            if (revLen <= 0)
                            {
                                Loger.Warn($"收到来自接收机\"{NetHelper.GetIPPortString(remoteEP)}\"的udp数据包长度为0,忽略.");
                                continue;
                            }

                            Interlocked.Increment(ref this._receiveCount);
                            Interlocked.Add(ref this._receiveDataLength, revLen);

                            if (this._isStoreData)
                            {
                                dataSeg.Update(0, revLen);
                                this._asyncWaitWriteDataSegPool.Enqueue(dataSeg);
                                this._asyncWaitWriteDataEventHandler.Set();
                                dataSeg = null;
                            }
                        }
                        catch (System.Net.Sockets.SocketException)
                        {

                        }
                        catch (ObjectDisposedException)
                        {

                        }
                        catch (Exception exi)
                        {
                            Loger.Error(exi);
                        }
                    }

                    while (this._asyncWaitWriteDataSegPool.TryDequeue(out dataSeg))
                    {
                        this._asyncWriteDataSegPool.Enqueue(dataSeg);
                    }
                }
                catch (Exception ex)
                {
                    Loger.Error(ex);
                }
            }


            private void WriteDataThreadMethod(ThreadPara threadPara)
            {
                try
                {
                    PtrSpan<byte> dataSeg;
                    Span<byte> buffer;
                    BinaryWriter writer = null;

                    while (!threadPara.Token.IsCancellationRequested)
                    {
                        if (!this._asyncWaitWriteDataSegPool.TryDequeue(out dataSeg))
                        {
                            this._asyncWaitWriteDataEventHandler.WaitOne(100);
                            continue;
                        }

                        try
                        {
                            if (this._packageHeadSize > 0)
                            {
                                buffer = new Span<byte>(dataSeg.DataPtr + this._packageHeadSize, dataSeg.Length - this._packageHeadSize);
                            }
                            else
                            {
                                buffer = dataSeg.GetSpan();
                            }

                            if (writer == null)
                            {
                                var filePath = PathEx.Combine(PathType.Local, this._storeDir, $"data_{DateTime.Now.ToString("yyyy-MM-dd_HH_mm_ss_fffffff")}.dat");
                                var fileStream = new FileStream(filePath, FileMode.CreateNew, FileAccess.Write);
                                writer = new BinaryWriter(fileStream);
                            }

                            if (this._dataFramMode)
                            {
                                writer.Write(buffer.Length);
                            }
                            writer.Write(buffer);

                            if (writer.BaseStream.Length >= this._singleFileByteLimit)
                            {
                                writer.Close();
                                writer = null;
                            }

                            Interlocked.Add(ref this._writeDataLength, dataSeg.Length);
                            Interlocked.Increment(ref this._writeCount);
                        }
                        catch (Exception exi)
                        {
                            Loger.Error(exi);
                        }
                        finally
                        {
                            this._asyncWriteDataSegPool.Enqueue(dataSeg);
                        }
                    }

                    if (writer != null)
                    {
                        writer.Close();
                        writer = null;
                    }
                }
                catch (Exception ex)
                {
                    Loger.Error(ex);
                }
            }

            public void Start(bool isStoreData, string storePath, long singleFileByteLimit, int durationMilliseconds)
            {
                if (isStoreData)
                {
                    if (string.IsNullOrWhiteSpace(storePath))
                    {
                        throw new ArgumentException("存储路径不能为空");
                    }

                    DirectoryInfoEx.CheckDirectory(storePath);
                }
                else
                {

                }

                this._isStoreData = isStoreData;
                this._storeDir = storePath;
                this._singleFileByteLimit = singleFileByteLimit;

                this._receiveDataLength = 0L;
                this._writeDataLength = 0L;
                this._waitRcvEndThread.Start(durationMilliseconds);

                this._receiveDataThread.Start();
                this._writeDataThread.Start();
                //this._udpClient.StartAsync();
            }

            public void Stop()
            {
                this._receiveDataThread.Stop();
                this._writeDataThread.Stop();
                this._waitRcvEndThread.Stop();
            }

            public void Dispose()
            {
                this._receiveDataThread.Dispose();
                this._writeDataThread.Dispose();
                this._asyncWaitWriteDataEventHandler.Set();
                this._asyncWaitWriteDataEventHandler.Dispose();
                this._waitRcvEndThread.Dispose();
            }

        }
    }


}
